﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace g4
{
    /// <summary>
    /// 区域操作器：自动从网格中提取子网格，支持编辑后重新插入回原始网格（前提是未破坏边界）
    /// 
    /// 注意：
    /// - 代码与RegionRemesher存在大量重复，可考虑重构为基类
    /// - ReinsertSubToBaseMapT（子网格到原始网格的三角形映射）并非由MeshEditor.ReinsertSubmesh直接返回，
    ///   而是通过假设推测得出，目前可用但需改进（理想情况下应由MeshEditor返回）
    /// </summary>
    public class RegionOperator
    {
        /// <summary>
        /// 原始基础网格（包含待操作的区域）
        /// </summary>
        public DMesh3 BaseMesh;

        /// <summary>
        /// 从基础网格中提取的子网格区域
        /// </summary>
        public DSubmesh3 Region;

        /// <summary>
        /// 子网格顶点到基础网格顶点的映射（仅在BackPropagate()调用后有效）
        /// </summary>
        public IndexMap ReinsertSubToBaseMapV;

        /// <summary>
        /// 子网格三角形到基础网格三角形的映射（当前实现为推测得出，存在一定hack性）
        /// </summary>
        public IndexMap ReinsertSubToBaseMapT;

        /// <summary>
        /// 处理重新插入时的重复三角形行为（默认：断言并继续）
        /// </summary>
        public MeshEditor.DuplicateTriBehavior ReinsertDuplicateTriBehavior = MeshEditor.DuplicateTriBehavior.AssertContinue;

        /// <summary>
        /// 当前基础网格中属于操作区域的三角形ID数组
        /// </summary>
        int[] cur_base_tris;


        /// <summary>
        /// 构造函数：从指定三角形集合初始化区域操作器
        /// </summary>
        /// <param name="mesh">基础网格</param>
        /// <param name="regionTris">构成操作区域的三角形ID数组</param>
        /// <param name="submeshConfigF">子网格配置委托（可选）</param>
        public RegionOperator(DMesh3 mesh, int[] regionTris, Action<DSubmesh3> submeshConfigF = null)
        {
            BaseMesh = mesh;
            Region = new DSubmesh3(mesh);
            if (submeshConfigF != null)
                submeshConfigF(Region); // 应用子网格配置
            Region.Compute(regionTris); // 计算子网格
            Region.ComputeBoundaryInfo(regionTris); // 计算边界信息

            cur_base_tris = (int[])regionTris.Clone(); // 保存初始区域三角形
        }

        /// <summary>
        /// 构造函数：从三角形集合（IEnumerable）初始化区域操作器
        /// </summary>
        /// <param name="mesh">基础网格</param>
        /// <param name="regionTris">构成操作区域的三角形ID集合</param>
        /// <param name="submeshConfigF">子网格配置委托（可选）</param>
        public RegionOperator(DMesh3 mesh, IEnumerable<int> regionTris, Action<DSubmesh3> submeshConfigF = null)
        {
            BaseMesh = mesh;
            Region = new DSubmesh3(mesh);
            if (submeshConfigF != null)
                submeshConfigF(Region); // 应用子网格配置
            Region.Compute(regionTris); // 计算子网格
            int count = regionTris.Count();
            Region.ComputeBoundaryInfo(regionTris, count); // 计算边界信息

            cur_base_tris = regionTris.ToArray(); // 保存初始区域三角形
        }


        /// <summary>
        /// 子区域三角形列表：初始为输入的regionTris，重新插入后更新为新的三角形ID
        /// </summary>
        public int[] CurrentBaseTriangles
        {
            get { return cur_base_tris; }
        }


        /// <summary>
        /// 查找基础网格中区域的内部顶点（不包含区域边界顶点）
        /// </summary>
        /// <returns>内部顶点ID的哈希集合</returns>
        public HashSet<int> CurrentBaseInteriorVertices()
        {
            HashSet<int> verts = new HashSet<int>();
            IndexHashSet borderVerts = Region.BaseBorderV; // 区域边界顶点

            // 遍历区域内所有三角形的顶点，筛选出非边界顶点
            foreach (int tid in cur_base_tris)
            {
                Index3i triVerts = BaseMesh.GetTriangle(tid);
                if (!borderVerts[triVerts.a]) verts.Add(triVerts.a);
                if (!borderVerts[triVerts.b]) verts.Add(triVerts.b);
                if (!borderVerts[triVerts.c]) verts.Add(triVerts.c);
            }
            return verts;
        }


        /// <summary>
        /// 修复可能的非流形边：处理子网格编辑中可能产生的非流形拓扑问题
        /// 
        /// 问题场景：子网格中两个边界顶点之间创建了内部边，而这两个顶点在基础网格中已存在连接边，
        /// 合并后会导致边的邻接三角形数超过2（非流形）。常见于"鳍状"三角形复制的情况。
        /// 
        /// 解决方法：分割子网格中的问题边，避免合并后产生非流形边
        /// </summary>
        public void RepairPossibleNonManifoldEdges()
        {
            // 收集需要分割的边
            List<int> splitEdges = new List<int>();
            int maxEid = Region.SubMesh.MaxEdgeID;
            for (int eid = 0; eid < maxEid; ++eid)
            {
                if (!Region.SubMesh.IsEdge(eid))
                    continue;
                if (Region.SubMesh.IsBoundaryEdge(eid)) // 跳过边界边
                    continue;

                Index2i edgeVerts = Region.SubMesh.GetEdgeV(eid);
                // 检查边的两个顶点是否都是子网格的边界顶点
                if (Region.SubMesh.IsBoundaryVertex(edgeVerts.a) &&
                    Region.SubMesh.IsBoundaryVertex(edgeVerts.b))
                {

                    // 映射到基础网格的顶点ID
                    int baseA = Region.MapVertexToBaseMesh(edgeVerts.a);
                    int baseB = Region.MapVertexToBaseMesh(edgeVerts.b);
                    if (baseA != DMesh3.InvalidID && baseB != DMesh3.InvalidID)
                    {
                        // 检查基础网格中是否已存在这条边
                        int baseEid = Region.BaseMesh.FindEdge(baseA, baseB);
                        if (baseEid != DMesh3.InvalidID)
                            splitEdges.Add(eid); // 标记为需要分割的边
                    }
                }
            }

            // 分割所有问题边
            foreach (int eid in splitEdges)
            {
                DMesh3.EdgeSplitInfo splitInfo;
                Region.SubMesh.SplitEdge(eid, out splitInfo);
            }
        }


        /// <summary>
        /// 为整个子网格设置三角形组ID
        /// </summary>
        /// <param name="gid">目标组ID</param>
        public void SetSubmeshGroupID(int gid)
        {
            FaceGroupUtil.SetGroupID(Region.SubMesh, gid);
        }


        /// <summary>
        /// 将编辑后的子网格重新插入回基础网格（先移除原始区域，再插入新子网格）
        /// 可多次调用，基础三角形集合会自动更新
        /// </summary>
        /// <param name="bAllowSubmeshRepairs">是否允许修复子网格以避免非流形边</param>
        /// <returns>插入是否完全成功（部分成功但存在错误时返回false）</returns>
        public bool BackPropropagate(bool bAllowSubmeshRepairs = true)
        {
            // 修复子网格（如需要）
            if (bAllowSubmeshRepairs)
            {
                RepairPossibleNonManifoldEdges();
            }

            // 移除基础网格中的原始区域三角形
            MeshEditor editor = new MeshEditor(BaseMesh);
            editor.RemoveTriangles(cur_base_tris, true);

            // 插入编辑后的子网格
            int[] newTris = new int[Region.SubMesh.TriangleCount];
            ReinsertSubToBaseMapV = null;
            bool success = editor.ReinsertSubmesh(Region, ref newTris, out ReinsertSubToBaseMapV, ReinsertDuplicateTriBehavior);

            // 构建子网格到基础网格的三角形映射（hack方式）
            int maxTriID = Region.SubMesh.MaxTriangleID;
            ReinsertSubToBaseMapT = new IndexMap(false, maxTriID);
            int triIndex = 0;
            for (int ti = 0; ti < maxTriID; ++ti)
            {
                if (Region.SubMesh.IsTriangle(ti))
                {
                    ReinsertSubToBaseMapT[ti] = newTris[triIndex++];
                }
            }

            // 验证新插入的三角形是否有效
            Debug.Assert(IndexUtil.IndicesCheck(newTris, BaseMesh.IsTriangle));

            // 更新当前区域三角形集合
            cur_base_tris = newTris;
            return success;
        }


        /// <summary>
        /// 将子网格中的顶点位置（及法向量）传递回基础网格
        /// </summary>
        /// <param name="bRecomputeBoundaryNormals">是否重新计算边界顶点的法向量</param>
        /// <returns>操作是否成功</returns>
        public bool BackPropropagateVertices(bool bRecomputeBoundaryNormals = false)
        {
            // 检查是否同时包含顶点法向量
            bool hasNormals = (Region.SubMesh.HasVertexNormals && Region.BaseMesh.HasVertexNormals);

            // 同步顶点位置和法向量
            foreach (int subVid in Region.SubMesh.VertexIndices())
            {
                int baseVid = Region.SubToBaseV[subVid];
                // 更新顶点位置
                Vector3d pos = Region.SubMesh.GetVertex(subVid);
                Region.BaseMesh.SetVertex(baseVid, pos);
                // 更新顶点法向量（如可用）
                if (hasNormals)
                {
                    Region.BaseMesh.SetVertexNormal(baseVid, Region.SubMesh.GetVertexNormal(subVid));
                }
            }

            // 重新计算边界顶点的法向量（如需要）
            if (bRecomputeBoundaryNormals)
            {
                foreach (int baseVid in Region.BaseBorderV)
                {
                    Vector3d normal = MeshNormals.QuickCompute(Region.BaseMesh, baseVid);
                    Region.BaseMesh.SetVertexNormal(baseVid, (Vector3f)normal);
                }
            }

            return true;
        }
    }
}